﻿/******************************************************************************
* SunnyUI.FrameDecoder 开源TCP、串口数据解码库。
* CopyRight (C) 2022-2023 ShenYongHua(沈永华).
* QQ群：56829229 QQ：17612584 EMail：SunnyUI@qq.com
*
* Blog:   https://www.cnblogs.com/yhuse
* Gitee:  https://gitee.com/yhuse/SunnyUI.FrameDecoder
*
* SunnyUI.FrameDecoder.dll can be used for free under the MIT license.
* If you use this code, please keep this note.
* 如果您使用此代码，请保留此说明。
******************************************************************************
* 文件名称: DataCache.cs
* 文件说明: 流式数据缓存
* 当前版本: V4.0
* 创建日期: 2023-01-28
*
* 2023-01-28: V4.0.0 增加文件说明
******************************************************************************/

using System;
using System.Buffers;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace Sunny.FrameDecoder
{
    /// <summary>
    /// 数据缓存
    /// </summary>
    /// <typeparam name="T">类型</typeparam>
    public class DataCache<T> : IDisposable, IDataCache<T>, IBufferWriter<T>
    {
        private T[] _buffer;
        private T[] _temp;
        private int _index;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="capacity">初始容量</param>
        public DataCache(int capacity = ConstDefine.CacheSize)
        {
            if (capacity <= 0) capacity = ConstDefine.CacheSize;

            _buffer = ArrayPool<T>.Shared.Rent(capacity);
            _temp = ArrayPool<T>.Shared.Rent(capacity);
        }


        #region IDisposable

        /// <summary>
        /// 是否释放标志，保证重复释放资源时，不重复释放
        /// </summary>
        public bool IsDisposed { get; private set; }

        /// <summary>
        /// 判断是否已经被释放，如果是，则抛出异常。
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void ThrowIfDisposed()
        {
#if NET8_0_OR_GREATER
            ObjectDisposedException.ThrowIf(IsDisposed, this.GetType());
#else
            if (IsDisposed) throw new ObjectDisposedException(this.GetType().FullName);
#endif
        }

        /// <summary>
        /// 实现IDisposable中的Dispose方法
        /// </summary>
        public void Dispose()
        {
            // 必须为true
            Dispose(true);

            // 通知GC垃圾回收机制不再调用终结器（析构器）
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// 释放对象
        /// </summary>
        /// <param name="disposing">ture时一般位主动调用释放，false一般为GC调用析构函数时释放</param>
        protected virtual void Dispose(bool disposing)
        {
            if (!IsDisposed && disposing)
            {
                IsDisposed = true;

                //释放资源
                ArrayPool<T>.Shared.Return(_buffer);
                ArrayPool<T>.Shared.Return(_temp);
            }
        }

        #endregion

        /// <summary>
        /// 标签
        /// </summary>
        public object Tag { get; set; }

        private void CheckAndResizeBuffer(int sizeHint)
        {
            ThrowIfDisposed();

            if (sizeHint + _index > _buffer.Length)
            {
                int len = _buffer.Length * 2;
                while (len < sizeHint + _index)
                {
                    len *= 2;
                }

                T[] tmp = ArrayPool<T>.Shared.Rent(len);
                _buffer.CopyTo(tmp, 0);
                ArrayPool<T>.Shared.Return(_buffer);
                _buffer = tmp;

                ArrayPool<T>.Shared.Return(_temp);
                _temp = ArrayPool<T>.Shared.Rent(len);
            }
        }

        /// <inheritdoc/>        
        public void Advance(int count)
        {
            ThrowIfDisposed();

            if (count < 0)
                throw new ArgumentException(null, nameof(count));

            if (_index > _buffer.Length - count)
                ThrowHelper.ThrowInvalidOperationException_AdvancedTooFar(_buffer.Length);

            _index += count;
        }

        /// <inheritdoc/>      
        public void Clear()
        {
            ThrowIfDisposed();
            _index = 0;
            _buffer.AsSpan().Clear();
        }

        /// <inheritdoc/>      
        public Memory<T> GetMemory(int sizeHint = 0)
        {
            CheckAndResizeBuffer(sizeHint);
            Debug.Assert(_buffer.Length > _index);
            return _buffer.AsMemory(_index);
        }

        /// <inheritdoc/>      
        public Span<T> GetSpan(int sizeHint = 0)
        {
            CheckAndResizeBuffer(sizeHint);
            Debug.Assert(_buffer.Length > _index);
            return _buffer.AsSpan(_index);
        }

        /// <inheritdoc/>      
        public void Remove(int length)
        {
            ThrowIfDisposed();
            if (length <= 0) return;
            if (length == _index)
            {
                Clear();
            }
            else
            {
                _buffer.AsSpan().Slice(length, _index - length).CopyTo(_temp.AsSpan());
                _index -= length;

                _buffer.AsSpan().Clear();
                _temp.AsSpan().Slice(0, _index).CopyTo(_buffer.AsSpan());
            }
        }

        /// <summary>
        /// 返回到目前为止写入基础缓冲区的数据.
        /// </summary>
        public ReadOnlyMemory<T> WrittenMemory
        {
            get
            {
                ThrowIfDisposed();
                return _buffer.AsMemory(0, _index);
            }
        }

        /// <summary>
        /// 返回到目前为止写入基础缓冲区的数据
        /// </summary>
        public ReadOnlySpan<T> WrittenSpan
        {
            get
            {
                ThrowIfDisposed();
                return _buffer.AsSpan(0, _index);
            }
        }

        /// <summary>
        /// 返回到目前为止写入基础缓冲区的数据量
        /// </summary>
        public int WrittenCount => _index;

        /// <summary>
        /// 返回基础缓冲区内的总空间量
        /// </summary>
        public int Capacity
        {
            get
            {
                ThrowIfDisposed();
                return _buffer.Length;
            }
        }

        /// <summary>
        /// 返回在不强制基础缓冲区增长的情况下仍可写入的可用空间量
        /// </summary>
        public int FreeCapacity
        {
            get
            {
                ThrowIfDisposed();
                return _buffer.Length - _index;
            }
        }

        /// <summary>
        /// 增加数据
        /// </summary>
        /// <param name="value">数据</param>
        public void Add(ReadOnlySpan<T> value)
        {
            ThrowIfDisposed();
            var span = GetSpan(value.Length);
            value.CopyTo(span);
            Advance(value.Length);
        }
    }
}
